<template lang='pug'>
  v-card
    v-toolbar(flat, color='primary', dark, dense)
      .subtitle-1 {{ $t('admin:utilities.importv1Title') }}
    v-card-text
      .text-center
        img.animated.fadeInUp.wait-p1s(src='/_assets/svg/icon-software.svg')
        .body-2 Import from Wiki.js 1.x
      v-divider.my-4
      .body-2 Data from a Wiki.js 1.x installation can easily be imported using this tool. What do you want to import?
      v-checkbox(
        label='Content + Uploads'
        value='content'
        color='deep-orange darken-2'
        v-model='importFilters'
        hide-details
        )
        template(v-slot:label)
          strong.deep-orange--text.text--darken-2 Content + Uploads
      .pl-8(v-if='wantContent')
        v-radio-group(v-model='contentMode', hide-details)
          v-radio(
            value='git'
            color='primary'
            )
            template(v-slot:label)
              div
                span Import from Git Connection
                .caption: em #[strong.primary--text Recommended] | The Git storage module will also be configured for you.
        .pl-8.mt-5(v-if='needGit')
          v-row
            v-col(cols='8')
              v-select(
                label='Authentication Mode'
                :items='gitAuthModes'
                v-model='gitAuthMode'
                outlined
                hide-details
              )
            v-col(cols='4')
              v-switch(
                label='Verify SSL Certificate'
                v-model='gitVerifySSL'
                hide-details
                color='primary'
              )
            v-col(cols='8')
              v-text-field(
                outlined
                label='Repository URL'
                :placeholder='(gitAuthMode === `ssh`) ? `e.g. git@github.com:orgname/repo.git` : `e.g. https://github.com/orgname/repo.git`'
                hide-details
                v-model='gitRepoUrl'
              )
            v-col(cols='4')
              v-text-field(
                label='Branch'
                placeholder='e.g. master'
                v-model='gitRepoBranch'
                outlined
                hide-details
              )
            v-col(v-if='gitAuthMode === `ssh`', cols='12')
              v-textarea(
                outlined
                label='Private Key Contents'
                placeholder='-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----'
                hide-details
                v-model='gitPrivKey'
              )
            template(v-else-if='gitAuthMode === `basic`')
              v-col(cols='6')
                v-text-field(
                  label='Username'
                  v-model='gitUsername'
                  outlined
                  hide-details
                )
              v-col(cols='6')
                v-text-field(
                  type='password'
                  label='Password / PAT'
                  v-model='gitPassword'
                  outlined
                  hide-details
                )
            v-col(cols='6')
              v-text-field(
                label='Default Author Email'
                placeholder='e.g. name@company.com'
                v-model='gitUserEmail'
                outlined
                hide-details
              )
            v-col(cols='6')
              v-text-field(
                label='Default Author Name'
                placeholder='e.g. John Smith'
                v-model='gitUserName'
                outlined
                hide-details
              )
            v-col(cols='12')
              v-text-field(
                label='Local Repository Path'
                placeholder='e.g. ./data/repo'
                v-model='gitRepoPath'
                outlined
                hide-details
              )
              .caption.mt-2 This folder should be empty or not exist yet. #[strong.deep-orange--text.text--darken-2 DO NOT] point to your existing Wiki.js 1.x repository folder. In most cases, it should be left to the default value.
          v-alert(color='deep-orange', outlined, icon='mdi-alert', prominent)
            .body-2 - Note that if you already configured the git storage module, its configuration will be replaced with the above.
            .body-2 - Although both v1 and v2 installations can use the same remote git repository, you shouldn't make edits to the same pages simultaneously.
        v-radio-group(v-model='contentMode', hide-details)
          v-divider
          v-radio.mt-3(
            value='disk'
            color='primary'
            )
            template(v-slot:label)
              div
                span Import from local folder
                .caption: em Choose this option only if you didn't have git configured in your Wiki.js 1.x installation.
        .pl-8.mt-5(v-if='needDisk')
          v-text-field(
            outlined
            label='Content Repo Path'
            hint='The absolute path to where the Wiki.js 1.x content is stored on disk.'
            persistent-hint
            v-model='contentPath'
          )

      v-checkbox(
        label='Users'
        value='users'
        color='deep-orange darken-2'
        v-model='importFilters'
        hide-details
        )
        template(v-slot:label)
          strong.deep-orange--text.text--darken-2 Users
      .pl-8.mt-5(v-if='wantUsers')
        v-text-field(
          outlined
          label='MongoDB Connection String'
          hint='The connection string to connect to the Wiki.js 1.x MongoDB database.'
          persistent-hint
          v-model='dbConnStr'
        )
        v-radio-group(v-model='groupMode', hide-details, mandatory)
          v-radio(
            value='MULTI'
            color='primary'
            )
            template(v-slot:label)
              div
                span Create groups for each unique user permissions configuration
                .caption: em #[strong.primary--text Recommended] | Users having identical permission sets will be assigned to the same group. Note that this can potentially result in a large amount of groups being created.
          v-divider
          v-radio.mt-3(
            value='SINGLE'
            color='primary'
            )
            template(v-slot:label)
              div
                span Create a single group with all imported users
                .caption: em The new group will have read permissions enabled by default.
          v-divider
          v-radio.mt-3(
            value='NONE'
            color='primary'
            )
            template(v-slot:label)
              div
                span Don't create any group
                .caption: em Users will not be able to access your wiki until they are assigned to a group.

        v-alert.mt-5(color='deep-orange', outlined, icon='mdi-alert', prominent)
          .body-2 Note that any user that already exists in this installation will not be imported. A list of skipped users will be displayed upon completion.
          .caption.grey--text You must first delete from this installation any user you want to migrate over from the old installation.

    v-card-chin
      v-btn.px-3(depressed, color='deep-orange darken-2', :disabled='!wantUsers && !wantContent', @click='startImport').ml-0
        v-icon(left, color='white') mdi-database-import
        span.white--text Start Import
    v-dialog(
      v-model='isLoading'
      persistent
      max-width='350'
      )
      v-card(color='deep-orange darken-2', dark)
        v-card-text.pa-10.text-center
          semipolar-spinner.animated.fadeIn(
            :animation-duration='1500'
            :size='65'
            color='#FFF'
            style='margin: 0 auto;'
          )
          .mt-5.body-1.white--text Importing from Wiki.js 1.x...
          .caption Please wait
          v-progress-linear.mt-5(
            color='white'
            :value='progress'
            stream
            rounded
            :buffer-value='0'
          )
    v-dialog(
      v-model='isSuccess'
      persistent
      max-width='350'
      )
      v-card(color='green darken-2', dark)
        v-card-text.pa-10.text-center
          v-icon(size='60') mdi-check-circle-outline
          .my-5.body-1.white--text Import completed
          template(v-if='wantUsers')
            .body-2
              span #[strong {{successUsers}}] users imported
              v-btn.text-none.ml-3(
                v-if='failedUsers.length > 0'
                text
                color='white'
                dark
                @click='showFailedUsers = true'
                )
                v-icon(left) mdi-alert
                span {{failedUsers.length}} failed
            .body-2 #[strong {{successGroups}}] groups created
        v-card-actions.green.darken-1
          v-spacer
          v-btn.px-5(
            color='white'
            outlined
            @click='isSuccess = false'
          ) Close
          v-spacer
    v-dialog(
      v-model='showFailedUsers'
      persistent
      max-width='800'
      )
      v-card(color='red darken-2', dark)
        v-toolbar(color='red darken-2', dense)
          v-icon mdi-alert
          .body-2.pl-3 Failed User Imports
          v-spacer
          v-btn.px-5(
            color='white'
            text
            @click='showFailedUsers = false'
            ) Close
        v-simple-table(dense, fixed-header, height='300px')
          template(v-slot:default)
            thead
              tr
                th Provider
                th Email
                th Error
            tbody
              tr(v-for='(fusr, idx) in failedUsers', :key='`fusr-` + idx')
                td {{fusr.provider}}
                td {{fusr.email}}
                td {{fusr.error}}
</template>

<script>
import _ from 'lodash'

import { SemipolarSpinner } from 'epic-spinners'

import utilityImportv1UsersMutation from 'gql/admin/utilities/utilities-mutation-importv1-users.gql'
import storageTargetsQuery from 'gql/admin/storage/storage-query-targets.gql'
import storageStatusQuery from 'gql/admin/storage/storage-query-status.gql'
import targetExecuteActionMutation from 'gql/admin/storage/storage-mutation-executeaction.gql'
import targetsSaveMutation from 'gql/admin/storage/storage-mutation-save-targets.gql'

export default {
  components: {
    SemipolarSpinner
  },
  data() {
    return {
      importFilters: ['content', 'users'],
      groupMode: 'MULTI',
      contentMode: 'git',
      dbConnStr: 'mongodb://',
      contentPath: '/wiki-v1/repo',
      isLoading: false,
      isSuccess: false,
      gitAuthMode: 'ssh',
      gitAuthModes: [
        { text: 'SSH', value: 'ssh' },
        { text: 'Basic', value: 'basic' }
      ],
      gitVerifySSL: true,
      gitRepoUrl: '',
      gitRepoBranch: 'master',
      gitPrivKey: '',
      gitUsername: '',
      gitPassword: '',
      gitUserEmail: '',
      gitUserName: '',
      gitRepoPath: './data/repo',
      progress: 0,
      successGroups: 0,
      successUsers: 0,
      successPages: 0,
      showFailedUsers: false,
      failedUsers: []
    }
  },
  computed: {
    wantContent () {
      return this.importFilters.indexOf('content') >= 0
    },
    wantUsers () {
      return this.importFilters.indexOf('users') >= 0
    },
    needDisk () {
      return this.contentMode === `disk`
    },
    needGit () {
      return this.contentMode === `git`
    }
  },
  methods: {
    async startImport () {
      this.isLoading = true
      this.progress = 0
      this.failedUsers = []

      _.delay(async () => {
        // -> Import Users

        if (this.wantUsers) {
          try {
            const resp = await this.$apollo.mutate({
              mutation: utilityImportv1UsersMutation,
              variables: {
                mongoDbConnString: this.dbConnStr,
                groupMode: this.groupMode
              }
            })
            const respObj = _.get(resp, 'data.system.importUsersFromV1', {})
            if (!_.get(respObj, 'responseResult.succeeded', false)) {
              throw new Error(_.get(respObj, 'responseResult.message', 'An unexpected error occurred'))
            }
            this.successUsers = _.get(respObj, 'usersCount', 0)
            this.successGroups = _.get(respObj, 'groupsCount', 0)
            this.failedUsers = _.get(respObj, 'failed', [])
            this.progress += 50
          } catch (err) {
            this.$store.commit('pushGraphError', err)
            this.isLoading = false
            return
          }
        }

        // -> Import Content

        if (this.wantContent) {
          try {
            const resp = await this.$apollo.query({
              query: storageTargetsQuery,
              fetchPolicy: 'network-only'
            })
            if (_.has(resp, 'data.storage.targets')) {
              this.progress += 10
              let targets = resp.data.storage.targets.map(str => {
                let nStr = {
                  ...str,
                  config: _.sortBy(str.config.map(cfg => ({
                    ...cfg,
                    value: JSON.parse(cfg.value)
                  })), [t => t.value.order])
                }

                // -> Setup Git Module

                if (this.contentMode === 'git' && nStr.key === 'git') {
                  nStr.isEnabled = true
                  nStr.mode = 'sync'
                  nStr.syncInterval = 'PT5M'
                  nStr.config = [
                    { key: 'authType', value: { value: this.gitAuthMode } },
                    { key: 'repoUrl', value: { value: this.gitRepoUrl } },
                    { key: 'branch', value: { value: this.gitRepoBranch } },
                    { key: 'sshPrivateKeyMode', value: { value: 'contents' } },
                    { key: 'sshPrivateKeyPath', value: { value: '' } },
                    { key: 'sshPrivateKeyContent', value: { value: this.gitPrivKey } },
                    { key: 'verifySSL', value: { value: this.gitVerifySSL } },
                    { key: 'basicUsername', value: { value: this.gitUsername } },
                    { key: 'basicPassword', value: { value: this.gitPassword } },
                    { key: 'defaultEmail', value: { value: this.gitUserEmail } },
                    { key: 'defaultName', value: { value: this.gitUserName } },
                    { key: 'localRepoPath', value: { value: this.gitRepoPath } },
                    { key: 'gitBinaryPath', value: { value: '' } }
                  ]
                }

                // -> Setup Disk Module
                if (this.contentMode === 'disk' && nStr.key === 'disk') {
                  nStr.isEnabled = true
                  nStr.mode = 'push'
                  nStr.syncInterval = 'P0D'
                  nStr.config = [
                    { key: 'path', value: { value: this.contentPath } },
                    { key: 'createDailyBackups', value: { value: false } }
                  ]
                }
                return nStr
              })

              // -> Save storage modules configuration

              const respSv = await this.$apollo.mutate({
                mutation: targetsSaveMutation,
                variables: {
                  targets: targets.map(tgt => _.pick(tgt, [
                    'isEnabled',
                    'key',
                    'config',
                    'mode',
                    'syncInterval'
                  ])).map(str => ({...str, config: str.config.map(cfg => ({...cfg, value: JSON.stringify({ v: cfg.value.value })}))}))
                }
              })
              const respObj = _.get(respSv, 'data.storage.updateTargets', {})
              if (!_.get(respObj, 'responseResult.succeeded', false)) {
                throw new Error(_.get(respObj, 'responseResult.message', 'An unexpected error occurred'))
              }

              this.progress += 10

              // -> Wait for success sync

              let statusAttempts = 0
              while (statusAttempts < 10) {
                statusAttempts++
                const respStatus = await this.$apollo.query({
                  query: storageStatusQuery,
                  fetchPolicy: 'network-only'
                })
                if (_.has(respStatus, 'data.storage.status[0]')) {
                  const st = _.find(respStatus.data.storage.status, ['key', this.contentMode])
                  if (!st) {
                    throw new Error('Storage target could not be configured.')
                  }
                  switch (st.status) {
                    case 'pending':
                      if (statusAttempts >= 10) {
                        throw new Error('Storage target is stuck in pending state. Try again.')
                      } else {
                        continue
                      }
                    case 'operational':
                      statusAttempts = 10
                      break
                    case 'error':
                      throw new Error(st.message)
                  }
                } else {
                  throw new Error('Failed to fetch storage sync status.')
                }
              }

              this.progress += 15

              // -> Perform import all

              const respImport = await this.$apollo.mutate({
                mutation: targetExecuteActionMutation,
                variables: {
                  targetKey: this.contentMode,
                  handler: 'importAll'
                }
              })

              const respImportObj = _.get(respImport, 'data.storage.executeAction', {})
              if (!_.get(respImportObj, 'responseResult.succeeded', false)) {
                throw new Error(_.get(respImportObj, 'responseResult.message', 'An unexpected error occurred'))
              }

              this.progress += 15
            } else {
              throw new Error('Failed to fetch storage targets.')
            }
          } catch (err) {
            this.$store.commit('pushGraphError', err)
            this.isLoading = false
            return
          }
        }

        this.isLoading = false
        this.isSuccess = true
      }, 1500)
    }
  }
}
</script>

<style lang='scss'>

</style>
